use std::fs::{File, create_dir_all};
use std::io::{self, Read, Write};
use std::path::Path;
use zip::write::{FileOptions, ZipWriter};
use zip::{CompressionMethod, ZipArchive};
use walkdir::WalkDir;

/// 工具类 `ZipUtil` 用于压缩和解压缩 ZIP 文件
pub struct ZipUtil;

impl ZipUtil {
    /// 压缩文件或目录到指定的 ZIP 文件
    ///
    /// # 参数
    /// * `src_dir` - 源目录路径
    /// * `dst_file` - 目标 ZIP 文件路径
    ///
    /// # 返回值
    /// 返回包含操作结果的 `Result<(), io::Error>`
    pub fn compress(src_dir: &str, dst_file: &str) -> Result<(), io::Error> {
        let path = Path::new(src_dir);
        let file = File::create(dst_file)?;
        let mut zip = ZipWriter::new(file);

        let options = FileOptions::default()
            .compression_method(CompressionMethod::Deflated)
            .unix_permissions(0o755);

        if path.is_file() {
            // 如果源路径是文件，则直接压缩文件
            ZipUtil::zip_file(path, path.file_name().unwrap().to_str().unwrap(), &mut zip, options)?;
        } else if path.is_dir() {
            // 如果源路径是目录，则递归压缩目录中的所有文件和子目录
            for entry in WalkDir::new(path) {
                let entry = entry?;
                let path = entry.path();
                let name = path.strip_prefix(Path::new(src_dir)).unwrap();

                if path.is_file() {
                    // 压缩文件
                    ZipUtil::zip_file(path, name.to_str().unwrap(), &mut zip, options)?;
                } else if name.as_os_str().len() != 0 {
                    // 添加空目录
                    zip.add_directory(name.to_str().unwrap(), options)?;
                }
            }
        }
        zip.finish()?;
        Ok(())
    }

    /// 解压缩 ZIP 文件到指定的目录
    ///
    /// # 参数
    /// * `src_file` - 源 ZIP 文件路径
    /// * `dst_dir` - 目标目录路径
    ///
    /// # 返回值
    /// 返回包含操作结果的 `Result<(), io::Error>`
    pub fn decompress(src_file: &str, dst_dir: &str) -> Result<(), io::Error> {
        let file = File::open(src_file)?;
        let mut archive = ZipArchive::new(file)?;

        for i in 0..archive.len() {
            let mut file = archive.by_index(i)?;
            let outpath = Path::new(dst_dir).join(file.sanitized_name());

            if file.name().ends_with('/') {
                // 如果是目录，则创建目录
                create_dir_all(&outpath)?;
            } else {
                // 如果是文件，则创建文件并写入内容
                if let Some(p) = outpath.parent() {
                    if !p.exists() {
                        create_dir_all(&p)?;
                    }
                }
                let mut outfile = File::create(&outpath)?;
                io::copy(&mut file, &mut outfile)?;
            }

            #[cfg(unix)]
            {
                // 设置文件权限（仅限 Unix 系统）
                use std::os::unix::fs::PermissionsExt;
                if let Some(mode) = file.unix_mode() {
                    std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode))?;
                }
            }
        }
        Ok(())
    }

    /// 将文件添加到 ZIP 压缩包中
    ///
    /// # 参数
    /// * `path` - 文件路径
    /// * `name` - 文件在 ZIP 压缩包中的名称
    /// * `zip` - ZIP 写入器
    /// * `options` - 文件选项
    ///
    /// # 返回值
    /// 返回包含操作结果的 `Result<(), io::Error>`
    fn zip_file(
        path: &Path,
        name: &str,
        zip: &mut ZipWriter<File>,  // 使用 `File` 类型确保实现了 `Seek` trait
        options: FileOptions,
    ) -> Result<(), io::Error> {
        let mut f = File::open(path)?;
        zip.start_file(name, options)?;
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer)?;
        zip.write_all(&buffer)?;
        Ok(())
    }
}

fn main() {
    let src_dir = "src_directory";
    let dst_zip = "archive.zip";
    let dst_dir = "dst_directory";

    // 压缩文件或目录
    match ZipUtil::compress(src_dir, dst_zip) {
        Ok(_) => log::info!("成功压缩 {} 到 {}", src_dir, dst_zip),
        Err(e) => log::info!("压缩 {} 失败：{}", src_dir, e),
    }

    // 解压缩 ZIP 文件
    match ZipUtil::decompress(dst_zip, dst_dir) {
        Ok(_) => log::info!("成功解压 {} 到 {}", dst_zip, dst_dir),
        Err(e) => log::info!("解压 {} 失败：{}", dst_zip, e),
    }
}
